#!/usr/bin/env python
#
# Copyright 2010 Sebible Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""A ManyToManyField class to make the user declare the relationsip between two django_ldap models easily"""


from django_ldap.exceptions import FieldInitException

class Field(object):
    pass

class ManyToManyField(Field):
    queryset_func = ['add', 'remove']
    def __init__(self, to, from_field=None, to_field=None, mode='from'):
        self._cached_field = None
        self.to = to
        self.to_field = to_field
        self.from_field = from_field
        self.mode = mode

    def get_class(self):
        from models import Model
        if isinstance(self.to, basestring):
            i = self.to.rfind('.')
            module, attr = self.to[:i], self.to[i+1:]
            try:
                mod = __import__(module, {}, {}, [attr])
            except ImportError, e:
                raise FieldInitException, 'Error importing field class %s: "%s"' % (module, e)
            try:
                return getattr(mod, attr)
            except AttributeError:
                raise FieldInitException, 'Module "%s" does not define a "%s" class' % (module, attr)            
        elif issubclass(self.to, Model):
            return self.to
        else:
            raise FieldInitException, 'the to param should be a string or a django-ldap model subclass'
    
    def get_queryset(self, obj):
        if self.mode in ['from','both']:
            queryset = self.get_class().objects.filter(**{self.from_field: obj.pk})
        else:
            attrs = getattr(obj, self.to_field)
            queryset = []
            for attr in attrs:
                try:
                    from_obj = self.get_class().get(pk=attr)
                    queryset.append(from_obj)
                except:
                    pass
        return queryset

    def set_queryset_functions(self, queryset, obj):
        for func in self.queryset_func:
            func_getter_name = 'get_%s_function' % func 
            setattr(queryset, func, getattr(self, func_getter_name)(obj))
    
    def get_add_function(self, obj):
        def add(*objs):
            if isinstance(objs, basestring):
                objs = [objs]
            for each_obj in objs:
                if isinstance(each_obj, basestring):
                    obj._add_relation(each_obj, self.from_field, self.to_field, self.mode)
                elif hasattr(each_obj, 'pk'):
                    obj._add_relation(each_obj.pk, self.from_field, self.to_field, self.mode)
        return add

    
    def get_remove_function(self, obj):
        def remove(*objs):
            if isinstance(objs, basestring):
                objs = [objs]
            for each_obj in objs:
                if isinstance(each_obj, basestring):
                    obj._del_relation(each_obj, self.from_field, self.to_field, self.mode)
                elif hasattr(each_obj, 'pk'):
                    obj._del_relation(each_obj.pk, self.from_field, self.to_field, self.mode)
        return remove
    
    def get_field(self, obj ,key):
        field = getattr(obj, '_related_cached__%s' % key, None)
        if not field:
            queryset = self.get_queryset(obj)
            self.set_queryset_functions(queryset, obj)
            field = queryset
            setattr(obj, '_related_cached__%s' % key, field)
        return field